package util;

import com.microsoft.sqlserver.jdbc.SQLServerException;
import java.sql.*;

/*
 * Class de conexão com a base de dados Ultima Modificação: 12:01 23/03/2012
 */
/**
 *
 * @author Bruno Manuel, Luis Lopes, Benjamim Lomanda & Mbungo Paulo
 */
public class Conexao {

    //variaveis para comunicação com a BD
    private Connection conn;
    private Statement statement;
    private PreparedStatement pstatement;
    private CallableStatement callable;
    private ResultSet resultSet;
    //driver para conexão
    public final static String mysqlDriver = "com.mysql.jdbc.Driver";
    public final static String sqlServerDriver = "com.microsoft.sqlserver.jdbc.SQLServerDriver";
    //paramentros para conexão
    private String driver;
    private String urlConexao;
    private String user;
    private String pass;
    // variavel para depuração
    private boolean debug;
    private boolean debugPrintTraces;

    //conexão personalizada identica automaticamente o caminho da base de dados 
    //basta reutilizar a variavel estática desta classe
    public Conexao(String nomeBaseDados, String user, String pass, String driver) {
        inicConexao();
        this.user = user;
        this.pass = pass;
        this.driver = driver;

        if (mysqlDriver.equalsIgnoreCase(driver)) {
            this.urlConexao = "jdbc:mysql://localhost:3306/" + nomeBaseDados;
        } else if (sqlServerDriver.equalsIgnoreCase(driver)) {
            this.urlConexao = "jdbc:sqlserver://localhost:1433;databaseName=" + nomeBaseDados;
        }
        debugPrintTraces = true;
    }

    public void setDebug(boolean debug) {
        this.debug = debug;
    }

    public void setDebugPrintTraces(boolean debug) {
        this.debugPrintTraces = debug;
    }

    private void inicConexao() {
        this.conn = null;
        this.statement = null;
        this.pstatement = null;
        this.resultSet = null;
        this.callable = null;

        this.driver = null;
        this.urlConexao = null;
        this.user = null;
        this.pass = null;
    }

    //conecta ou abre a conexao com a base de dados
    public boolean iniciar() {
        boolean estado = false;
        if (conn == null) {
            try {
                Class.forName(driver);
                conn = DriverManager.getConnection(urlConexao, user, pass);
                if (debug) {
                    System.out.println("INFO: Conexão obtida com sucesso.");
                }
                estado = true;
            } catch (ClassNotFoundException ex) {
                if (debug) {
                    System.err.println("Sem Driver da Conexão: " + ex.getMessage());
                }
            } catch (SQLException ex) {
                if (debug) {
                    System.err.println("SQLException: " + ex.getMessage());
                }
            } catch (Exception ex) {
                if (debug) {
                    System.err.println("Problemas ao tentar conectar com o banco de dados: " + ex.getMessage());
                }
            } finally {
                return estado;
            }
        } else {
            fecharConexao();
            conn = null;
            return iniciar();
        }
    }

    //inicializa um preparedstatment
    public boolean prepararInstrucao(String intrucao) {
        boolean estado = false;
        try {
            pstatement = conn.prepareStatement(intrucao);
            if (debug) {
                System.out.println("INFO: Intrução pre-preparada criada.");
            }
            estado = true;
        } catch (SQLException ex) {
            System.err.println("Erro ao criar pre-preparada Intrução:" + ex.getMessage());
            if (debugPrintTraces) {
                ex.printStackTrace();
            }
        } finally {
            return estado;
        }
    }

    //inicializa um statment    
    public boolean criarInstrucao() {
        boolean estado = false;
        try {
            statement = conn.createStatement();
            if (debug) {
                System.out.println("INFO: Intrução criada.");
            }
            estado = true;
        } catch (SQLException ex) {
            System.err.println("Erro ao criar Intrução:" + ex.getMessage());
            if (debugPrintTraces) {
                ex.printStackTrace();
            }

        } finally {
            return estado;
        }
    }

    public boolean prepararProcedimento(String procedimento) {
        boolean estado = false;
        try {
            callable = conn.prepareCall(procedimento);
            if (debug) {
                System.out.println("INFO: Procedimento preparado.");
            }
            estado = true;
        } catch (SQLException ex) {
            System.err.println("Erro ao preparar procedimento:" + ex.getMessage());
            if (debugPrintTraces) {
                ex.printStackTrace();
            }

        } finally {
            return estado;
        }
    }

    // executa a instrução do prepare statement caso
    public boolean executarInstrucao() {
        return executarInstrucao("");
    }

    //executa instrução mesmo sem prepara ou criar
    // verificar o retorno para instruções de inserção e actualização
    // Só será retornado false se houver algum caso de erro
    public boolean executarInstrucao(String instrucao) {
        boolean estado = true;
        try {

            if (statement != null && !instrucao.isEmpty()) {

                resultSet = statement.executeQuery(instrucao);
                if (debug) {
                    System.out.println("INFO: Intrução executada com sucesso.");
                }
            } else if (pstatement != null && instrucao.isEmpty()) {

                pstatement.execute();
                if (pstatement.getResultSet() != null) {
                    resultSet = pstatement.getResultSet();
                }

                if (debug) {
                    System.out.println("INFO: Intrução pre-preparada executada com sucesso.");
                }
            } else if (!instrucao.isEmpty()) {
                criarInstrucao();
                estado = executarInstrucao(instrucao);
            }
        } catch (SQLServerException ex) {
            if (ex.getMessage().contains("statement did not return a result")) {
                if (debug) {
                    System.out.println("INFO: Intrução executada mas sem retorno ao resulset!!!\n\tVerifica a sua query!");
                    resultSet = null;
                }
            } else {
                System.err.println("Erro ao executar a Instrução: " + ex.getMessage());
                estado = false;
            }
        } finally {
            return estado;
        }
    }

    public boolean executarProcedimento() {
        boolean estado = false;
        try {
            if (callable != null) {
                callable.execute();
                estado = true;
                if (debug) {
                    System.out.println("INFO: Procedimento Executado com Sucesso.");
                }
            } else {
                System.err.println("Callable nulo, tem que preparar primeiro!!!");
            }
        } catch (SQLException ex) {
            System.err.println("Erro ao executar o procedimento: " + ex.getMessage());
            if (debugPrintTraces) {
                ex.printStackTrace();
            }

        } finally {
            return estado;
        }
    }

    public boolean executarProcedimentoComRetorno() {
        boolean estado = false;
        try {
            if (callable != null) {
                resultSet = callable.executeQuery();
                estado = true;
                if (debug) {
                    System.out.println("INFO: Procedimento Executado e Resultset retornado com sucesso.");
                }
            } else {
                System.err.println("Callable nulo, tem que preparar primeiro!!!");
            }
        } catch (SQLException ex) {
            System.err.println("Erro ao executar o procedimento"
                    + " resultSet com retorno ao : " + ex.getMessage());
            if (debugPrintTraces) {
                ex.printStackTrace();
            }

        } finally {
            return estado;
        }
    }

    //retorna o resultset OBS: tem que verificar s está nullo
    public CallableStatement getCallable() {
        if (debug) {
            System.out.println("INFO: Callable retornado " + callable == null ? "nulo!!" : ".");
        }
        return callable;
    }

    //retorna a intrução preparada
    public PreparedStatement getStatement() {
        if (statement != null) {
            if (debug) {
                System.out.println("INFO: Statment retornado");
            }
            return (PreparedStatement) statement;
        } else if (pstatement != null) {
            if (debug) {
                System.out.println("INFO: Prepared statment retornado");
            }
            return pstatement;
        }
        if (debug) {
            System.out.println("INFO: Statment retornado nulo");
        }
        return null;
    }

    //retorna o resultset OBS: tem que verificar s está nullo
    public ResultSet getResultSet() {
        if (debug) {
            System.out.println("INFO: ResultSet retornado" + resultSet == null ? " nulo!!" : ".");
        }
        return resultSet;
    }

    public void fecharConexao() {
        try {
            if (conn != null) {
                conn.close();
                conn = null;
            }
            if (debug) {
                System.out.println("INFO: Conexão fechada com sucesso...");
            }
        } catch (SQLException ex) {
            System.err.println("Erro ao fechar a Conexão:" + ex.getMessage());
            if (debugPrintTraces) {
                ex.printStackTrace();
            }

        }
    }

    private void fecharInstrucoes() {
        String info = "";
        try {
            if (statement != null) {
                statement.close();
                statement = null;
                info += "INFO: statement fechado com sucesso..\n";
            }

            if (pstatement != null) {
                pstatement.close();
                statement = null;
                info += "INFO: preparedStatement fechado com sucesso..";
            }
        } catch (SQLException ex) {
            System.err.println("Erro ao fechar a Conexão:" + ex.getMessage());
            if (debugPrintTraces) {
                ex.printStackTrace();
            }

        } finally {
            if (debug) {
                System.out.println(info);
            }
        }
    }

    public void fecharTudo() {
        try {
            if (resultSet != null) {
                resultSet.close();
                resultSet = null;
                if (debug) {
                    System.out.println("INFO: resultSet fechado com sucesso.");
                }
            }

            fecharInstrucoes();

            fecharConexao();

        } catch (SQLException ex) {
            System.err.println("Erro ao fechar componentes da conexão:" + ex.getMessage());
            if (debugPrintTraces) {
                ex.printStackTrace();
            }

        }
    }

    @Override
    protected void finalize() throws Throwable {
        fecharTudo();
        super.finalize();
    }
}
